Core DNS
개요
코어 DNS는 쿠버네티스에서 사용되는 DNS 서버 애드온이다.
원래는 별개의 CNCF 프로젝트이나, 실질적으로 이거 말고 쓰이는 다른 대안이 없어 그냥 쿠버네티스 DNS라고 해도 상관 없다.
그래서 이 문서에서는 쿠버네티스 dns 방식에 대한 일체의 설명을 곁들인다.[1]
4.RESOURCE/KNOWLEDGE/Kubernetes/클러스터/클러스터에서 서비스에도, 파드도 고유한 ip를 부여받는다.
근데 통신할 때 ip 쓰는 찐따송구스럽다가 어딨냐 ㅋㅋ
당연히 쿠버네티스도 내부에서 통신할 때 DNS를 활용할 수 있도록 설계됐다.
원래 쿠버네티스에는 kube-dns라는 dns 서버가 어플리케이션이 있었다.
근데 이걸 조금 더 확장하는 애드온이 현재 거의 표준이 돼버린 것이다.
그래서 kube-system
에서 확인해보면 kube-dns
라는 이름의 서비스를 확인할 수 있다.
이를 통해 추후에 다른 dns 애드온을 사용한다고 해도 클러스터의 모든 dns는 kube-dns 서비스를 사용하므로 문제가 없게 된다.
클러스터 도메인
클러스터의 모든 파드, 서비스는 기본적으로 도메인이 부여된다.
이들만이 내부에서 IP를 가지니까 어찌보면 당연한 것 같기도
각 대상에게 어떤 식으로 도메인이 부여되는지부터 보자면..
- 서비스
- A/AAAA레코드 - {서비스 이름}.{네임스페이스}.svc.cluster.local
- SRV 레코드 - _{포트이름}._{포트 프로토콜}.{서비스 이름}.{네임스페이스}.svc.cluster.local
- 파드
- A/AAAA레코드 - {파드-ip}.{네임스페이스}.pod.cluster.local
- 가령 default 네임스페이스의 파드가
10.10.10.10
의 ip를 가졌다면10-10-10-10.default.pod.cluster.local
과 같은 식이 된다.
- 가령 default 네임스페이스의 파드가
- A/AAAA레코드 - {hostname}.{subdomain}.{네임스페이스}.svc.cluster.local
- hostname, subdomain 필드를 설정하고 헤드리스 서비스에 연결될 경우 생기는 도메인이다.
- hostname 없이 subdomain만 지정하면 a레코드만 만들어준다.
- T-스테이트풀셋과 연결되는 헤드리스 서비스에 관한 실험에서 내가 맘대로 실험한 게 그래서 실패한 것이다.
- 이때 헤드리스 서비스는 CNAME 레코드 역할을 수행한다.
- hostname, subdomain 필드를 설정하고 헤드리스 서비스에 연결될 경우 생기는 도메인이다.
- A/AAAA레코드 - {파드-ip}.{네임스페이스}.pod.cluster.local
여기에서 cluster.local은 클러스터 도메인으로, 클러스터를 구축하는 시점에 설정할 수 있다. 근데 커스텀하는 사람을 본 적이 없다
이렇게 지정된 도메인 네임을 반드시 FQDN(Fully Qualified Domain Name)으로 전부 나열해서 호출할 필요는 없다.
- 같은 네임스페이스라면 - 뒤에 다 생략하고 서비스 이름만 이용해 질의 가능
- 다른 네임스페이스라면 - 네임스페이스 부분까지는 쓰고 뒷 부분은 생략 가능
아직 DNS 문서를 안 만들어서 남기는데, 이건 사용되는 프로토콜과 포트까지 함께 리턴한다.
한 도메인에 여러 서비스가 있을 때 사용하면 좋다.
ip 별 가중치도 부여할 수 있는, 일반 a 레코드의 확장 버전이랄까.
여담으로 .spec.setHostnameAsFQND=True
스펙이 돼있으면 파드 내부에서 hostname을 조회했을 때 FQDN이 나온다.
근데, 원래 리눅스에서는 호스트네임이 64글자 이상 될 수 없기 때문에 FQDN이 이상 넘어간다면 에러가 난다.
이런 걸 체크할 때는 Admission Webhook을 사용하는 게 좋다.
도메인 질의 구조
그래서 이 도메인을 어떻게 IP와 매칭시킬 것인가?
당연히 DNS 서버에 질의를 해야 한다.
그런데 이 도메인 네임은 당연히 클러스터 내부에서만 사용되기 때문에 클러스터 내부 DNS 서버에 질의를 해야 한다.
이때 해당 질의를 받는 서버가 바로 Core DNS이다.
각 파드가 생성될 때 kubelet은 /etc/resolv.conf
에 클러스터 도메인이라 Core DNS에게 질의해야 하는 도메인에 대한 설정을 넣어준다.
nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
이런 식으로 설정을 넣기 때문에 같은 네임스페이스에서는 뒤를 다 떼어먹고, 다른 네임스페이스면 네임스페이스까지만 붙여넣어서 질의가 가능한 것이다.
가령 default 네임스페이스의 파드가 naver.com 이란 도메인에 대해 질의를 한다고 생각해보자.
그럼 실제로 다음의 dns 질의들이 날아가게 된다.
- naver.com
- naver.com.default
- naver.com.com.default.svc
- naver.com.com.default.svc.cluster
ndots 5개(루트 도메인 포함) 제한으로 여기까지 질의하게 될 것이다.
이러한 방식은 당연히 DNS 서버에 과도한 부하를 안길 수 있기 때문에 ndots 값을 작게 유지하는 것이 최적화 전략 중 하나이다.
ndots는 도메인을 FQDN으로 인식하기 위한 점의 개수를 설정하는 옵션으로, 5개라고 한다면 도메인에 .이 5개 찍힐 때 FQDN으로 인식하고 뒤에 search 절의 도메인들을 붙여보는 실험을 하지 않는다.
그럼 실제 질의는 어떻게 이뤄질까?
아주 간단하게 도메인 질의 과정을 보자면 이런 식으로 이뤄진다.
(참고로 초록색 2번 플로우에서 파드 B는 절대로 저런 도메인을 가질 리 없지만 보기 편하라고 저렇게 적었다.)
클러스터의 컨테이너들은 자신 파일시스템에 /etc/resolv.conf
의 내용을 바탕으로 질의할 DNS 서버를 정한다.
해당 값은 kubelet이 생성 시점에 넣어주는 것으로, 싫다면 kubelet 플래그에 --resolv-conf=""
를 설정해서 커스텀 해주면 된다.
아무튼 이 resolv.conf 파일에는 항상 10.96.0.10
과 같은 식으로 무조건 Core dns의 서비스 IP가 적힌다.
그래서 모든 도메인 질의는 무조건 Core DNS를 통해 이뤄지게 된다.
이때 Core DNS는 받은 도메인에 따라 크게 두 가지 동작을 하게 된다.
- 클러스터 내부 주소
- 클러스터 내부 도메인은 클러스터 생성 시점에 설정할 수 있음(기본값:
*.cluster.local
) - 평소 core dns는 kube-apiserver를 감시하면서 서비스나 파드의 정보를 감시하고 있다.
- 그래서 이를 기반으로 파드나 서비스의 도메인이 들어왔을 때 해당 ip를 알려줄 수 있다.
- 클러스터 내부 도메인은 클러스터 생성 시점에 설정할 수 있음(기본값:
- 클러스터 외부 주소
- 클러스터 도메인이 아니면 노드의
/etc/resolve.conf
정보를 이용한다. - 여기에는 노드가 사용하는 도메인 서버가 적혀있을 거라 결과적으로 업스트림 네임 서버로 트래픽을 forward한다.[2]
- 클러스터 도메인이 아니면 노드의
Core DNS 설정
다시 설명하자면 Core DNS는 클러스터의 DNS 서버이다.
클러스터를 구축할 때 kubeadm 등으로 같이 설치할지 옵션을 넣을 수 있으며, 일반 디플로이먼트로 배포된다.
달리 말하자면 CNI가 배치되지 않으면 IP를 부여받지 못하고 pending 상태에 머무른다.
Core DNS의 설정은 ConfigMap으로 관리된다.
DNS 서버 워크로드 자체는 이 설정을 마운팅하여 기동되기에 동적으로 설정이나 기능을 넣을 수 있다.[3]
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
이런 식으로 kube-system
네임스페이스에 coredns
라는 이름으로 ConfigMap을 넣어주면 된다.
각 절에 대한 설명을 조금 해보자면..
- errors
- 에러에 대한 표준 출력 정의
- health
- 코어dns 서버에 대한 헬스체크로,
http://localhost:8080/health
경로로 수행된다. - lameduck은 프로세스가 종료될 때 unhealthy로 체크 후 5초 대기해준다는 것이다.
- 코어dns 서버에 대한 헬스체크로,
- ready
- 8181 포트가 열려있을 때 준비 상태로 둔다.
- kubernetes
- 쿠버네티스의 서비스, 파드에 대한 쿼리 설정 블록.
- ttl은 쿼리 캐싱으로, 기본 5초이며 0~3600 사이의 값으로 설정할 수 있다.
- pods
- insecure는 kube-dns의 호환성을 위한 옵션이다.
- verified를 주면 같은 네임스페이스의 요청은 무조건 A레코드를 준다.
- disabled는 파드 레코드를 쓰지 않을 때 사용하면 된다.
- Prometheus는 말 그대로
http://localhost:9153/metrics
에 프로메테우스 형식의 메트릭을 노출해준다. - forward는 core dns가 처리할 수 없는 도메인에 대해 외부로 쿼리를 보낼 resolve.conf 파일 경로를 지정한다.
- 그냥 노드의 resolve.conf 파일 경로를 제공하면 된다.
- cache는 프론트엔드 캐시를 가능케 한다...?
- loop는 루프가 발생했을 때 이를 중단시켜준다.
- reload는 이 파일이 수정됐을 때 동적으로 리로딩을 가능케 하는 옵션이다.
- 최대 2분까지 걸릴 수 있다.
- loadbalance는 dns 로드밸런싱을 해주는 기능이다.
여기다 추가 설정을 해도 된다.
consul.local:53 {
errors
cache 30
forward . 10.150.0.1
}
Consul을 사용한다면 10.150.0.1에 dns 서버가 새로 서는데, 이쪽으로 dns 쿼리를 설정해주는 방식이다.
참고사항.
coredns는 스텁도메인 및 네임서버에 대한 fqdn을 지원하지 않는다.
fqdn 네임서버 설정은 전부 생략된다고 한다.
기타 - 파드 DNS 설정
여기는 파드에 DNS 관련 설정 필드들에 대해 간략하게 다룬다.
정책
파드마다 .spec.dnsPolicy
필드를 통해 DNS 정책을 지정할 수 있다.
- Default - 파드의 resolv.conf가 노드의 파일을 기본적으로 사용한다.
- ClusterFirsrt - 클러스터 도메인에 맞지 않는 도메인에 대해서는 상위 dns 서버로 보낸다.
- 클러스터 구성에 따라 상위 네임 서버를 사용하는 경우가 있을 수 있다.
- ClusterFirstWithHostNet - 호스트 네트워크로 돌아가는 파드에 대해서 적용.
- 호스트 네트워크 파드에 위의 옵션들을 주면 Default로 동작하게 된다.
- None - 쿠버 환경의 dns 세팅을 무시하고 파드
.spec.dnsConfig
필드의 값으로 설정된다.
헷갈리면 안 되는 게, 아무 설정 안 하면 기본값이 ClusterFirst
다;;
Default
에 낚이면 안 된다..
설정
파드에 resolve.conf
에 추가되는 문자열 설정인데, 위의 정책이 None
일 때는 필수적으로 설정돼야 한다.
dnsPolicy: "None"
dnsConfig:
nameservers:
- 192.0.2.1 # this is an example
searches:
- ns1.svc.cluster-domain.example
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
이 이렇게 된 예시는 아래처럼 resolv.conf
에 추가된다.
nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0
options 부분은 조금 설명이 필요할 수 있겠다.
ndots는 FQDN으로 간주할 범위를 말한다.
2로 돼있으니, foo.bar.
라는 식의 쿼리가 나오면 뒤에 추가 도메인을 붙이지 않고 바로 네임서버로 질의를 날린다.
edns0은 dns 프로토콜을 확장할 수 있게 해주는 기능이라고 한다.
dns 검색 도메인 리스트 제한
쿠버에서는 도메인 리스트가 32개를 넘거나, 전체 도메인의 길이가 2048자를 넘기전까지는 설정에 대한 제한을 크게 하지 않는다.
이 제한은 노드, 파드, dns 설정 파일에 각각 적용된다.
근데 컨테이너 런타임마다 이에 대한 제한이 개별 적용돼있을 수 있어서, 이 제한과 충돌이 일어나는 경우가 있었다고 한다.
Containerd 1.5.5, CRI-O 1.21에서 이런 이슈가 있었다고 한다.
관련 문서
이름 | noteType | created |
---|---|---|
Core DNS | knowledge | 2025-01-09 |
S-flannel dns 질의 실패 | topic/shooting | 2024-09-11 |